package com.paradogs.framework.core.annoations.rpc;

import com.paradogs.framework.core.events.RPCRequestEvent;
import com.paradogs.framework.core.utils.EventUtils;
import com.paradogs.framework.core.utils.RouteUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.CompletableFuture;

/**
 * @author: yumi
 * @date: 2023/8/13  9:40
 * @Description: TODO
 */
@Slf4j
public class PRRPCProxyInterceptor implements MethodInterceptor {

    /**
     * 执行方法拦截器
     * @param obj 被代理的目标对象实例对象，该对象为cglib代理实现，为自定义实现类
     * @param method 被代理接口正在执行的方法
     * @param args 被代理接口正在执行方法的参数
     * @param methodProxy methodProxy.invokeSuper() ?执行原本方法
     * @return 方法执行结果，如果方法无返回值，可以返回 null
     * 待优化：目前每次执行都会反射找到 Class，完全没必要，可以项目启动时初始化成 Map，和 Controller 的一样
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        try {
            PRRPCMapping PRRPCMapping = AnnotationUtils.findAnnotation(method, PRRPCMapping.class);
            if (PRRPCMapping == null) {
                return methodProxy.invokeSuper(obj, args);
            }

            // 获取被代理目标的真实 class
            Class<?> targetClass = obj.getClass();
            boolean aopProxy = AopUtils.isAopProxy(obj);
            if (aopProxy) {
                targetClass = AopUtils.getTargetClass(obj);
            }

            PRClient PRClient = AnnotationUtils.findAnnotation(targetClass, PRClient.class);

            // 获得参数消息
            Object requestData = null;
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                com.paradogs.framework.core.annoations.request.PRMsg requestAnnotation = AnnotationUtils.findAnnotation(parameters[i], com.paradogs.framework.core.annoations.request.PRMsg.class);
                if (requestAnnotation != null) {
                    try {
                        requestData = args[i];
                    } catch (Exception e) {
                        throw new RuntimeException("@PdgMsg arg must extends BaseMsg: " + method.getName());
                    } finally {
                        break;
                    }
                }
            }
            // 如果只有一个参数，则默认视为被 @PRMsg 修饰
            if (requestData == null && parameters.length == 1) {
                requestData = args[0];
            }

            // 获得返回值类型
            Class<?> returnType = method.getReturnType();
            /**
             * 监听回调
             */
            CompletableFuture<?> promise = new CompletableFuture<>();
            // 解耦 MsgUtils
            EventUtils.publishEvent(new RPCRequestEvent(
                    PRClient.value() + RouteUtils.SERVER_SEPARATOR + PRRPCMapping.value(),
                    requestData,
                    returnType,
                    promise
            ));

            Object result = promise.get();// 阻塞等待
            return result; // 类型不对应可能会导致回调失败并且没有抛异常

        } catch (Exception e) {
            log.error("{}", e.getMessage(), e);
        }
        return null;
    }
}
